//
// Created by hamlet on 23-4-18.
//

#include "Gram.h"
#include <fmt/core.h>
#include <ranges>
#include <stack>

//#define PRINT_MATCH
//#define PRINT_DEDUCTION
//#define PRINT_RESULT_GRAM

using namespace std;

using tk = TokenType;

const std::unordered_map<TokenType, std::string> Gram::tokenName{
        {tk::Null,       "未识别字符"},
        {tk::Program,    "program"},
        {tk::Begin,      "begin"},
        {tk::End,        "end"},
        {tk::Var,        "var"},
        {tk::If,         "if"},
        {tk::Then,       "then"},
        {tk::Else,       "else"},
        {tk::Do,         "do"},
        {tk::While,      "while"},
        {tk::Id,         "id"},
        {tk::Integer,    "integer"},
        {tk::Boolean,    "boolean"},
        {tk::ValueInt,   "整形常量"},
        {tk::True,       "true"},
        {tk::False,      "false"},
        {tk::OpPlus,     "+"},
        {tk::OpMinus,    "-"},
        {tk::OpMultiply, "*"},
        {tk::OpDivide,   "/"},
        {tk::OpEq,       "="},
        {tk::OpGreater,  ">"},
        {tk::OpLess,     "<"},
        {tk::OpAnd,      "&"},
        {tk::OpOr,       "|"},
        {tk::OpNot,      "!"},
        {tk::OpLPar,     "("},
        {tk::OpRPar,     ")"},
        {tk::OpLBigPar,  "{"},
        {tk::OpRBigPar,  "}"},
        {tk::OpSep,      ";"},
        {tk::OpComma,    ","},
        {tk::OpColon,    ":"},
        {tk::OpAssign,   ":="},
        {tk::Print,      "print"},
        {tk::P,          "P"},
        {tk::S,          "S"},
        {tk::S1,         "S1"},
        {tk::S2,         "S2"},
        {tk::S2_,        "S2'"},
        {tk::E,          "E"},
        {tk::E_,         "E'"},
        {tk::T,          "T"},
        {tk::T_,         "T'"},
        {tk::F,          "F"},
        {tk::E1,         "E1"},
        {tk::E1_,        "E1'"},
        {tk::E2,         "E2"},
        {tk::E2_,        "E2'"},
        {tk::T2,         "T2"},
        {tk::IdList,     "idlist"},
        {tk::IdList_,    "idlist'"},
        {tk::Type,       "type"},
        {tk::Printable,  "printable"},
        {tk::M,          "M"},
        {tk::N,          "N"},
};

void Gram::deduction(std::stack<std::shared_ptr<Token>> &st, TokenType t, int index) {
#ifdef PRINT_DEDUCTION
    string s;
    if (pdList[index].empty()) {
        s += " ε";
    } else {
        for (const auto &ty: pdList[index]) {
            s += ' ';
            s += tokenName.find(ty)->second;
        }
    }
    fmt::print("已推导产生式：{} →{}\n", tokenName.find(t)->second, s);
#endif
    auto parent = st.top();
    st.pop();
    int value = (t == tk::S || t == tk::S2 || t == tk::S2_) ? 1 : 0;
    for (auto it: pdList[index]) {
        auto tk = std::make_shared<Token>(it, value, parent);
        parent->children.push_back(tk);
    }
    for (const auto &tk: std::ranges::reverse_view(parent->children)) {
        st.emplace(tk);
    }
}

const std::vector<std::vector<TokenType>> Gram::pdList{
        {tk::Program,    tk::Id,       tk::OpSep,     tk::S2,     tk::OpSep}, //0
        {tk::OpSep}, //1
        {tk::Var,        tk::IdList,   tk::OpColon,   tk::Type,   tk::OpSep}, //2
        {tk::Id,         tk::OpAssign, tk::E,         tk::OpSep}, //3
        {tk::Id,         tk::OpAssign, tk::E1,        tk::OpSep}, //4
        {tk::If,         tk::E2,       tk::Then,      tk::M,      tk::S2, tk::S2_, tk::OpSep}, //5
        {tk::While,      tk::M,        tk::E2,        tk::Do,     tk::M,  tk::S2,  tk::OpSep}, //6
        {tk::Print,      tk::OpLPar,   tk::Printable, tk::OpRPar, tk::OpSep}, //7
        {tk::S,          tk::M,        tk::S1}, //8
        {}, //9
        {tk::Begin,      tk::S1,       tk::End}, //10
        {tk::OpLBigPar,  tk::S1,       tk::OpRBigPar}, //11
        {tk::N,          tk::Else,     tk::M,         tk::S2}, //12
        {tk::T,          tk::E_}, //13
        {tk::OpPlus,     tk::T,        tk::E_}, //14
        {tk::OpMinus,    tk::T,        tk::E_}, //15
        {tk::F,          tk::T_}, //16
        {tk::OpMultiply, tk::F,        tk::T_}, //17
        {tk::OpDivide,   tk::F,        tk::T_}, //18
        {tk::Id}, //19
        {tk::OpLPar,     tk::E,        tk::OpRPar}, //20
        {tk::E,          tk::E1_}, //21
        {tk::OpGreater,  tk::E}, //22
        {tk::OpLess,     tk::E}, //23
        {tk::OpEq,       tk::E}, //24
        {tk::T2,         tk::E2_}, //25
        {tk::OpNot,      tk::T2,       tk::E2_}, //26
        {tk::OpAnd,      tk::M,        tk::T2,        tk::E2_}, //27
        {tk::OpOr,       tk::M,        tk::T2,        tk::E2_}, //28
        {tk::E1}, //29
        {tk::OpLPar,     tk::E2,       tk::OpRPar}, //30
        {tk::Id,         tk::IdList_}, //31
        {tk::OpComma,    tk::IdList}, //32
        {tk::Integer}, //33
        {tk::Boolean}, //34
        {tk::ValueInt}, //35
        {tk::True}, //36
        {tk::False}, //37
        {tk::E}, //38
        {tk::E2}, //39
        {tk::S2,         tk::S1}, //40
};

void Gram::printType(TokenType t) {
#ifdef PRINT_RESULT_GRAM
    switch (t) {
        case tk::Id:
            fmt::print("⭐赋值语句\n");
            break;
        case tk::Var:
            fmt::print("⭐声明语句\n");
            break;
            // 无法在此处判断if语句是否跟有else
//        case tk::If:
//            fmt::print("⭐if分支语句\n");
//            break;
        case tk::While:
            fmt::print("⭐while-do循环语句\n");
            break;
        case tk::Print:
            fmt::print("⭐print打印语句\n");
            break;
        default:;
    }
#endif
}

const std::unordered_map<TokenType, std::unordered_map<TokenType, int>> Gram::preTable{
        {tk::P,       {
                              {tk::Program, 0},
                      }},
        {tk::S,       {
                              {tk::OpSep,   1},  {tk::Var,       2},  {tk::If,         5},  {tk::While,    6},  {tk::Print,  7},
                      }},
        {tk::S1,      {
                              {tk::OpSep,   8},  {tk::Var,       8},  {tk::Id,         8},  {tk::If,       8},  {tk::While,  8},  {tk::Print, 8}, {tk::End,       9},  {tk::OpRBigPar, 9},  {tk::OpLBigPar, 40},
                      }},
        {tk::S2,      {
                              {tk::Begin,   10}, {tk::OpLBigPar, 11},
                      }},
        {tk::S2_,     {
                              {tk::OpSep,   9},  {tk::Var,       9},  {tk::Id,         9},  {tk::If,       9},  {tk::Else,   12}, {tk::While, 9}, {tk::Print,     9},
                      }},
        {tk::E,       {
                              {tk::Id,      13}, {tk::OpLPar,    13}, {tk::ValueInt,   13},
                      }},
        {tk::E_,      {
                              {tk::OpPlus,  14}, {tk::OpMinus,   15}, {tk::OpRPar,     9},  {tk::OpAnd,    9},  {tk::OpOr,   9},  {tk::Then,  9}, {tk::Do,        9},  {tk::OpSep,     9},  {tk::OpGreater, 9}, {tk::OpLess, 9}, {tk::OpEq,      9},
                      }},
        {tk::T,       {
                              {tk::Id,      16}, {tk::OpLPar,    16}, {tk::ValueInt,   16},
                      }},
        {tk::T_,      {
                              {tk::OpPlus,  9},  {tk::OpMinus,   9},  {tk::OpMultiply, 17}, {tk::OpDivide, 18}, {tk::OpRPar, 9},  {tk::OpAnd, 9}, {tk::OpOr,      9},  {tk::Then,      9},  {tk::Do,        9}, {tk::OpSep,  9}, {tk::OpGreater, 9}, {tk::OpLess, 9}, {tk::OpEq, 9},
                      }},
        {tk::F,       {
                              {tk::Id,      19}, {tk::OpLPar,    20}, {tk::ValueInt,   35},
                      }},
        {tk::E1,      {
                              {tk::OpLPar,  21}, {tk::True,      36}, {tk::False,      37},
                      }},
        {tk::E1_,     {
                              {tk::OpRPar,  9},  {tk::OpAnd,     9},  {tk::OpOr,       9},  {tk::Then,     9},  {tk::Do,     9},  {tk::OpSep, 9}, {tk::OpGreater, 22}, {tk::OpLess,    23}, {tk::OpEq,      24},
                      }},
        {tk::E2,      {
                              {tk::Id,      25}, {tk::OpLPar,    25}, {tk::OpNot,      26}, {tk::True,     25}, {tk::False,  25},
                      }},
        {tk::E2_,     {
                              {tk::OpRPar,  9},  {tk::OpAnd,     27}, {tk::OpOr,       28}, {tk::Then,     9},  {tk::Do,     9},
                      }},
        {tk::T2,      {
                              {tk::Id,      29}, {tk::True,      29}, {tk::False,      29},
                      }},
        {tk::IdList,  {
                              {tk::Id,      31},
                      }},
        {tk::IdList_, {
                              {tk::OpComma, 32}, {tk::OpColon,   9},
                      }},
        {tk::Type,    {
                              {tk::Integer, 33}, {tk::Boolean,   34}
                      }}
};

bool Gram::analyze(istream &in, Lex &lex) {
    try {
        lex.init();
        root = std::make_shared<Token>(tk::P);
        std::stack<std::shared_ptr<Token>> st;
        st.push(root);
        std::shared_ptr<Token> t;
        std::shared_ptr<Token> tk = lex.getToken(in);
        static const auto deduct = [&st, &t](int index) {
            deduction(st, t->type, index);
        };
        while (!st.empty()) {
            t = st.top();
            if (t->type == tk->type) {
                if (t->type == TokenType::Id || t->type == TokenType::ValueInt) {
                    t->value = tk->value;
                }
#ifdef PRINT_MATCH
                fmt::print("已匹配终结符：{}\n", tokenName.find(t->type)->second);
#endif
                st.pop();
                tk = lex.getToken(in);
            } else if (t->type < tk::P) {
                if (t->value) {
                    throw ExcUnmatchedTerm(t->type, tk->type);
                } else {
                    fmt::print("出错，已抛弃底层错误\n");
                    st.pop();
                }
            } else {
                if (t->type == tk::M || t->type == tk::N) {
                    deduct(9);
                } else if (t->type == tk::S && tk->type == tk::Id) {
                    // TODO 判断变量的类型   (3 or 4)
#ifdef PRINT_RESULT_GRAM
                    printType(tk->type);
#endif
                    deduct(3);
                } else if (t->type == tk::E1 && tk->type == tk::Id) {
                    // TODO 判断变量的类型   (21 or 19)
                    deduct(21);
                } else if (t->type == tk::T2 && tk->type == tk::OpLPar) {
                    // TODO 判断是否是E2    (29 or 30)
                    deduct(29);
                } else if (t->type == tk::Printable) {
                    // TODO 判断是否是E    (38 or 39)
                    deduct(38);
                } else {
                    auto it1 = preTable.find(t->type);
                    bool fd = false;
                    if (it1 != preTable.end()) {
                        auto it2 = it1->second.find(tk->type);
                        if (it2 != it1->second.end()) {
#ifdef PRINT_RESULT_GRAM
                            if (t->type == tk::S) {
                                printType(tk->type);
                            }
                            if (t->type == tk::S2_) {
                                fmt::print("⭐if-then{}分支语句\n", it2->second == 9 ? "" : "-else");
                            }
#endif
                            deduct(it2->second);
                            fd = true;
                        }
                    }
                    if (!fd) {
                        if (t->value) {
                            if (t->type == tk::S2_) {
                                throw ExcUnmatchedTerm(tk::OpSep, tk->type);
                            } else if (t->type == tk::S2) {
                                throw ExcUnmatchedTerm(tk::OpLBigPar, tk->type);
                            } else {
                                throw ExcUnmatchedTerm(t->type, tk->type);
                            }
                        } else {
                            fmt::print("出错，已抛弃底层错误，{}\n", lex.getPos());
                            st.pop();
                        }
                    }
                }
            }
        }
    } catch (Lex::ExcEOF &) {
#ifdef PRINT_RESULT_GRAM
        fmt::print("已成功完成分析！\n");
#endif
        return true;
    } catch (Lex::ExcLex &e) {
        fmt::print("词法错误：{}，{}\n", e.what(), lex.getPos());
        return false;
    } catch (ExcGram &e) {
        fmt::print("语法错误：{}，{}\n", e.what(), lex.getPos());
        return false;
    } catch (std::exception &e) {
        fmt::print("未知错误：{}，{}\n", e.what(), lex.getPos());
    }
    return false;
}

void Gram::printSyntaxTree(const std::weak_ptr<Token> &node) {
    int count = static_cast<int>(node.use_count());
    auto root_shared = node.lock();
    fmt::print("{}({})：", tokenName.find(root_shared->type)->second, count);
    for (const auto &child: root_shared->children) {
        fmt::print("{}，", tokenName.find(child->type)->second);
    }
    fmt::print("\n");
    for (std::weak_ptr<Token> child: root_shared->children) {
        printSyntaxTree(child);
    }
}

const std::string &Gram::getTokenName(TokenType t) {
    return tokenName.find(t)->second;
}

Gram::ExcUnmatchedTerm::ExcUnmatchedTerm(TokenType expected, TokenType actual)
        : desc_(fmt::format("不匹配的符号[期望为{}，实际为{}]", getTokenName(expected), getTokenName(actual))) {}

const char *Gram::ExcUnmatchedTerm::what() const noexcept {
    return desc_.c_str();
}
